<?php
/**
 * Note:
 * 支付时, 随机选一个可用payChannel, 使用其配置;
 * 通知时, 先查询订单信息, 根据订单中的 payChannel, 进行后续校验(查订单等);
 * 要求各个商户配置的公私钥相同, 方便前期的校验通过.
 */
namespace App\Services\Pay;

use App\Abstracts\PayAbstract;
use App\Caches\Game\AppSourceCache;
use App\Caches\Game\PayChannelCache;
use App\Exceptions\PayChannelEmptyException;
use App\Models\Game\Center\PayOrder;
use App\Models\Game\Center\RefundOrder;
use App\Models\Game\Center\WithdrawOrder;
use Illuminate\Support\Str;
use Yansongda\Pay\Exceptions\GatewayException;
use Yansongda\Pay\Exceptions\InvalidConfigException;
use Yansongda\Pay\Exceptions\InvalidSignException;
use Yansongda\Pay\Pay;

class PayAlipay extends PayAbstract
{
    public const CHANNEL = 'alipay';

    /**
     * @var \Yansongda\Pay\Provider\Alipay
     */
    public $payment;

    /**
     * 生成网关
     * @param $order
     * @return \Yansongda\Pay\Provider\Alipay
     * @throws PayChannelEmptyException
     */
    public function getPaymentByOrder($order): \Yansongda\Pay\Provider\Alipay
    {
        $appSource = AppSourceCache::get($order->source);
        if ($order instanceof PayOrder) {
            $this->payChannel = $this->getUseablePayChannel(self::CHANNEL, $order->price, $appSource->type);
        } else if ($order instanceof WithdrawOrder) {
            $this->payChannel = $this->getUseableWithdrawChannel(self::CHANNEL, $order->money, $appSource->type);
        } else if ($order instanceof RefundOrder) {
            $this->payChannel = $this->getUseableWithdrawChannel(self::CHANNEL, $order->money);
        } else {
            throw new \Exception('invalid param: order');
        }
        $config = $this->getConfig($this->payChannel);
        $this->payment = Pay::alipay($config);
        return $this->payment;
    }

    /**
     * 根据 channelId 获取 payment
     * @param int $payChannelId
     * @return \Yansongda\Pay\Provider\Alipay
     */
    public function getPaymentByPayChannelId(int $payChannelId): \Yansongda\Pay\Provider\Alipay
    {
        $this->payChannel = PayChannelCache::get($payChannelId);
        $config = $this->getConfig($this->payChannel);
        $this->payment = Pay::alipay($config);
        return $this->payment;
    }

    /**
     * 获取测试权益实例
     * @return \Yansongda\Pay\Provider\Alipay
     * @throws \Exception
     */
    public function getGatewayDev(): \Yansongda\Pay\Provider\Alipay
    {
        $payChannels = PayChannelCache::getChannels(self::CHANNEL);
        $this->payChannel = null;
        foreach ($payChannels as $payChannel) {
            if ($payChannel->sandbox) {
                $this->payChannel = $payChannel;
                break;
            }
        }
        if (!$this->payChannel) throw new PayChannelEmptyException('无可用沙盒支付通道');
        $config = $this->getConfig($this->payChannel);
        $this->payment = Pay::alipay($config);
        return $this->payment;
    }

    /**
     * 校验
     * @param PayOrder $payOrder
     * @return bool
     */
    public function payOrderVerify(PayOrder $payOrder): bool
    {
        try {
            $orderData = $this->payment->find($payOrder->orderid);
            if (!in_array($orderData->trade_status, ['TRADE_SUCCESS', 'TRADE_FINISHED'])) {
                throw new \Exception('订单状态校验失败');
            }
        } catch (\Exception $e) {
            report($e);
            return false;
        }
        return true;
    }

    /**
     * 权益商户
     * @param string|null $subCode
     * @param string|null $subMsg
     * @return bool
     */
    public function withdrawMerchantFailed(string $subCode = null, string $subMsg = null): bool
    {
        if (!$subCode && !$subMsg) return false;
        //$subCodes = [
        //    //'BLOCK_USER_FORBBIDEN_RECIEVE',
        //    //'BLOCK_USER_FORBBIDEN_SEND',
        //    'EXCEED_LIMIT_MM_AMOUNT',
        //    'MONEY_PAY_CLOSED',
        //    'NO_ACCOUNT_PAYMENT_PERMISSION',
        //    'NO_AVAILABLE_PAYMENT_TOOLS',
        //    'PAYCARD_UNABLE_PAYMENT',
        //    'PAYER_NOT_EXIST',
        //    'PAYER_STATUS_ERROR',
        //    'PAYER_PERMLIMIT_CHECK_FAILURE',
        //    'PRODUCT_NOT_SIGN',
        //    'PERMIT_PAYER_FORBIDDEN',
        //];
        //if (in_array($subCode, $subCodes)) return true;

        $subMsgs = ['余额不足', '额度超限', '超出单日转账限额', '超出单月转账限额'];
        if (Str::contains($subMsg, $subMsgs)) return true;

        return false;
    }

    /**
     * 权益用户
     * @param string|null $subCode
     * @param string|null $subMsg
     * @return bool
     */
    public function withdrawUserFailed(string $subCode = null, string $subMsg = null): bool
    {
        if (!$subCode && !$subMsg) return false;

        $subMsgs = ['收款'];
        if (Str::contains($subMsg, $subMsgs)) return true;

        return false;
    }

    /**
     * 权益订单检查
     * @param WithdrawOrder $withdrawOrder
     * @return array status reason torderid
     * @throws InvalidConfigException
     * @throws InvalidSignException
     */
    public function withdrawOrderQuery(WithdrawOrder $withdrawOrder): array
    {
        $payment = $this->getPaymentByPayChannelId($withdrawOrder->pay_channel_id);

        try {
            $result = $payment->find($withdrawOrder->orderid, 'transfer');
        }  catch (GatewayException $e) {
            info($e->raw);
            $result = new \Yansongda\Supports\Collection(array_shift($e->raw));
        }

        $torderid = $result->order_id ?? '';
        $reason = $result->sub_msg ?: '';

        // 服务不可用, 稍候重试
        if ($result->code === '20000') return [WithdrawOrder::STATUS_PROCESSING, $reason, $torderid];
        // 订单不存在
        if ($result->code !== '10000') return [WithdrawOrder::STATUS_FAILED, $reason, $torderid];

        $reason = $result->fail_reason ?: '';

        // 等待处理/处理中视为处理中
        if ($result->status === 'INIT' || $result->status === 'DEALING') {
            return [WithdrawOrder::STATUS_PROCESSING, $reason, $torderid];
        } else if ($result->status !== 'SUCCESS') {
            return [WithdrawOrder::STATUS_FAILED, $reason, $torderid];
        }
        return [WithdrawOrder::STATUS_SUCCESS, $reason, $torderid];
    }

}
